#pragma once

#include <mutex>
#include "util.hpp"

namespace cloud
{
#define CONFIG_FILE "./cloud.conf" // 对配置文件的路径进行一个宏替换

    // 配置文件加载的单例类实现：将一些程序运行的关键信息加载到配置文件中集中管理，使得程序的运行更加灵活。
    // 使用单例模式（这里采用懒汉模式）管理系统配置信息，能够让配置信息的管理控制更加统⼀灵活。
    class Config
    {
    public:
        // 获取各配置信息的接口
        int GetHotTime()
        {
            return _hot_time;
        }
        int GetServerPort()
        {
            return _server_port;
        }
        std::string GetServerIp()
        {
            return _server_ip;
        }
        std::string GetDownloadPrefix()
        {
            return _download_prefix;
        }
        std::string GetPackFileSuffix()
        {
            return _packfile_suffix;
        }
        std::string GetPackDir()
        {
            return _pack_dir;
        }
        std::string GetBackDir()
        {
            return _back_dir;
        }
        std::string GetBackupFile()
        {
            return _backup_file;
        }

        // 获取单例对象的接口，也是静态的，通过指定类域就可以直接访问
        // 因为非静态成员函数要通过对象调用，而该接口就是来创建唯一的单例对象的
        static Config *GetInstance()
        {
            // 双检查加锁
            // 外层这个if判断是为了防止后续的获取在这里频繁的申请和释放锁，导致效率降低
            // 加锁只是为了保护第一次new
            if (_instance == nullptr)
            {
                _mutex.lock();
                // 这里是存在线程安全问题的，多线程场景下可能出现多个线程并发的走到这new
                // 为了保证线程安全，我们要加锁
                if (_instance == nullptr)
                {
                    _instance = new Config;
                }
                _mutex.unlock();
            }
            return _instance;
        }

    private:
        Config() // 构造函数私有化，限制类外面随意创建对象，实现单例！
        {
            // 构造函数里调用ReadConfigFile()即可完成对成员变量的初始化
            ReadConfigFile();
        }
        static Config *_instance; // 存放单例对象的指针
        static std::mutex _mutex;

        // 要加载到配置文件的各种信息
        int _hot_time;                // 热点判断时间，超过这个时间则认为是非热点文件
        int _server_port;             // 服务器的端口号
        std::string _server_ip;       // 服务器的IP地址
        std::string _download_prefix; // 文件下载请求的url前缀（来表明客户端的请求是一个下载请求）
        std::string _packfile_suffix; // 压缩包的后缀名
        std::string _pack_dir;        // 压缩包存放目录
        std::string _back_dir;        // 服务端备份文件的存放目录
        std::string _backup_file;     // 服务端备份文件信息的存放文件

        bool ReadConfigFile()
        {
            // 读取配置文件中的信息赋给我们的成员变量
            FileUtil fu(CONFIG_FILE); // 拿配置文件构建一个FileUtil对象

            std::string body;
            if (fu.GetContent(body) == false) // 调用GetContent接口获取文件内容放入字符串body
            {
                cout << "load config file failed!" << endl;
                return false;
            }

            // 而配置文件我们填充的是一个json对象，所以body就是一个json格式的字符串
            // 所以我们就可以调用JsonUtil类的UnSerialize接口进行反序列化转化成Json::Value对象
            Json::Value root;
            if (JsonUtil::UnSerialize(body, root) == false)
            {
                cout << "parse config file failed!" << endl;
                return false;
            }

            // 然后就可以获取root的各个字段赋给我们的成员变量
            _hot_time = root["hot_time"].asInt();
            _server_port = root["server_port"].asInt();
            _server_ip = root["server_ip"].asString();
            _download_prefix = root["download_prefix"].asString();
            _packfile_suffix = root["packfile_suffix"].asString();
            _pack_dir = root["pack_dir"].asString();
            _back_dir = root["back_dir"].asString();
            _backup_file = root["backup_file"].asString();

            return true;
        }
    };

    // 静态成员变量的初始化一定要在类外，类外定义时不添加static关键字（注意一定要指定类域），类中只是声明
    Config *Config::_instance = nullptr; // 懒汉模式是在第一次使用实例对象时，再创建对象，所以初始化先给个nullptr
    std::mutex Config::_mutex;
}
